package cn.edu.hitsz.compiler.lexer;

import cn.edu.hitsz.compiler.NotImplementedException;
import cn.edu.hitsz.compiler.symtab.SymbolTable;
import cn.edu.hitsz.compiler.utils.FileUtils;

import javax.swing.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.StreamSupport;

/**
 * TODO: 实验一: 实现词法分析
 * <br>
 * 你可能需要参考的框架代码如下:
 *
 * @see Token 词法单元的实现
 * @see TokenKind 词法单元类型的实现
 */
public class LexicalAnalyzer {
    private final SymbolTable symbolTable;
    private String fileContent;
    private List<Token> tokens;
    private final int MAXLEN = 63;

    public LexicalAnalyzer(SymbolTable symbolTable) {
        this.symbolTable = symbolTable;
    }


    /**
     * 从给予的路径中读取并加载文件内容
     *
     * @param path 路径
     */
    public void loadFile(String path) {
        // TODO: 词法分析前的缓冲区实现
        // 可自由实现各类缓冲区
        // 或直接采用完整读入方法
        fileContent = FileUtils.readFile(path);
    }

    /**
     * 执行词法分析, 准备好用于返回的 token 列表 <br>
     * 需要维护实验一所需的符号表条目, 而得在语法分析中才能确定的符号表条目的成员可以先设置为 null
     */
    public void run() {
        // TODO: 自动机实现的词法分析过程
        // 三个状态仅为区分字符串的第一个字符, 如果是数字则为IntConst, 字母则可能为关键字或id, 特殊字符不组成字符串
        enum State {empty, digit, letter}
        tokens = new ArrayList<>();
        char[] tmpString = new char[MAXLEN];
        State state;
        State nextState = State.empty;
        int index = 0;
        for (char c : fileContent.toCharArray()) {
            state = nextState;
            switch (state) {
                case empty -> {
                    if (Character.isDigit(c)) {
                        nextState = State.digit;
                        tmpString[index++] = c;
                    } else if (Character.isLetter(c) || c == '_') {
                        nextState = State.letter;
                        tmpString[index++] = c;
                    } else {
                        // index = 0;
                        processSpecialSymbol(c);
                    }
                }
                case digit -> {
                    // 第一个字符为数字, 为IntConst
                    if (Character.isDigit(c)) {
                        // nextState = State.digit;
                        tmpString[index++] = c;
                    } else if (Character.isLetter(c)) {
                        throw new RuntimeException("failed in compile");
                    } else {
                        nextState = State.empty;
                        String str = String.valueOf(Arrays.copyOf(tmpString, index));
                        index = 0;
                        tokens.add(Token.normal(TokenKind.fromString("IntConst"), str));
                        processSpecialSymbol(c);
                    }
                }
                case letter -> {
                    // 第一个字符为字母, 可能为关键字或id
                    if (Character.isLetterOrDigit(c)) {
                        // nextState = State.letter;
                        tmpString[index++] = c;
                    } else {
                        nextState = State.empty;
                        String str = String.valueOf(Arrays.copyOf(tmpString, index));
                        index = 0;
                        if (TokenKind.isAllowed(str)) {
                            // 尝试添加关键字
                            tokens.add(Token.simple(TokenKind.fromString(str)));
                        } else {
                            // 添加id
                            tokens.add(Token.normal(TokenKind.fromString("id"), str));
                            // 添加符号表
                            if (!symbolTable.has(str)) {
                                symbolTable.add(str);
                            }
                        }
                        processSpecialSymbol(c);
                    }
                }
            }
        }
        tokens.add(Token.eof());
    }

    private void processSpecialSymbol(char c) {
        if (c == ';') {
            tokens.add(Token.simple(TokenKind.fromString("Semicolon")));
        } else if (c != ' ' && c != '\n' && c != '\t' && c != '\r') {
            tokens.add(Token.simple(TokenKind.fromString(String.valueOf(c))));
        }
    }

    /**
     * 获得词法分析的结果, 保证在调用了 run 方法之后调用
     *
     * @return Token 列表
     */
    public Iterable<Token> getTokens() {
        // TODO: 从词法分析过程中获取 Token 列表
        // 词法分析过程可以使用 Stream 或 Iterator 实现按需分析
        // 亦可以直接分析完整个文件
        // 总之实现过程能转化为一列表即可
        return tokens;
    }

    public void dumpTokens(String path) {
        FileUtils.writeLines(
                path,
                StreamSupport.stream(getTokens().spliterator(), false).map(Token::toString).toList()
        );
    }


}
